package com.pingan.haofang.searchcloud.monitor.service.impl;

import java.math.BigDecimal;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.persistence.EntityManager;

import org.apache.commons.collections.CollectionUtils;
import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerUtils;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONObject;
import com.pingan.haofang.searchcloud.common.constants.Constants;
import com.pingan.haofang.searchcloud.common.constants.Status;
import com.pingan.haofang.searchcloud.common.dto.PageDTO;
import com.pingan.haofang.searchcloud.common.dto.PageQueryDTO;
import com.pingan.haofang.searchcloud.common.utils.DateUtils;
import com.pingan.haofang.searchcloud.dashboard.dto.ProgressAggregateDto;
import com.pingan.haofang.searchcloud.dashboard.service.BasicMonitorService;
import com.pingan.haofang.searchcloud.index.constants.IndexBuildRunType;
import com.pingan.haofang.searchcloud.index.constants.IndexBuildStatus;
import com.pingan.haofang.searchcloud.index.constants.IndexBuildType;
import com.pingan.haofang.searchcloud.index.dao.IndexBuildProgressDao;
import com.pingan.haofang.searchcloud.index.dao.IndexDao;
import com.pingan.haofang.searchcloud.index.domain.Index;
import com.pingan.haofang.searchcloud.index.dto.IndexBuildProgressDTO;
import com.pingan.haofang.searchcloud.index.dto.IndexBuildProgressTopDTO;
import com.pingan.haofang.searchcloud.index.service.IndexBuildProgressService;
import com.pingan.haofang.searchcloud.indexdatasource.dto.ScheduleTaskDto;
import com.pingan.haofang.searchcloud.indexdatasource.facade.IndexDatasourceScheduleFacade;
import com.pingan.haofang.searchcloud.mail.constants.MailConstants;
import com.pingan.haofang.searchcloud.mail.dto.MailDto;
import com.pingan.haofang.searchcloud.monitor.constants.MonitorIndicator;
import com.pingan.haofang.searchcloud.monitor.constants.WarningType;
import com.pingan.haofang.searchcloud.monitor.dao.DashboardIndexSummaryInfoDao;
import com.pingan.haofang.searchcloud.monitor.dao.DashboardWarningHistoryDao;
import com.pingan.haofang.searchcloud.monitor.domain.DashboardIndexSummaryInfo;
import com.pingan.haofang.searchcloud.monitor.domain.DashboardWarningConfig;
import com.pingan.haofang.searchcloud.monitor.domain.DashboardWarningHistory;
import com.pingan.haofang.searchcloud.monitor.dto.ChartReportResultDTO;
import com.pingan.haofang.searchcloud.monitor.dto.ChartReportXYDTO;
import com.pingan.haofang.searchcloud.monitor.dto.ChartReportXYMapDTO;
import com.pingan.haofang.searchcloud.monitor.dto.DashboardIndexAggregationDTO;
import com.pingan.haofang.searchcloud.monitor.dto.DashboardIndexRequestDTO;
import com.pingan.haofang.searchcloud.monitor.dto.DashboardIndexSummaryInfoChartResult;
import com.pingan.haofang.searchcloud.monitor.dto.DashboardIndexSummaryInfoDTO;
import com.pingan.haofang.searchcloud.monitor.dto.DashboardIndexSummaryInfoQueryResult;
import com.pingan.haofang.searchcloud.monitor.dto.DashboardIndexTopQueryResult;
import com.pingan.haofang.searchcloud.monitor.dto.IndexBuildProgressItemDTO;
import com.pingan.haofang.searchcloud.monitor.dto.IndexChartItemDTO;
import com.pingan.haofang.searchcloud.monitor.dto.WarningModelDTO;
import com.pingan.haofang.searchcloud.monitor.service.DashboardIndexSummaryInfoService;
import com.pingan.haofang.searchcloud.monitor.service.DashboardWarningConfigService;
import com.pingan.haofang.searchcloud.quartz.dto.TaskQueryDto;
import com.pingan.haofang.searchcloud.user.dao.SearchProjectDao;
import com.pingan.haofang.searchcloud.user.domain.SearchProject;

/**
 * DashboardIndexSummaryInfoServiceImpl
 *
 * @author yangbaohu662
 */
@Service
public class DashboardIndexSummaryInfoServiceImpl extends BasicMonitorService
        implements DashboardIndexSummaryInfoService {

    Logger LOG = LoggerFactory.getLogger(DashboardIndexSummaryInfoServiceImpl.class);

    @Autowired
    private DashboardIndexSummaryInfoDao indexSummaryInfoDao;

    @Autowired
    private IndexBuildProgressDao indexBuildProgressDao;

    @Autowired
    private IndexDao indexDao;

    @Autowired
    private IndexDatasourceScheduleFacade indexDatasourceScheduleFacade;

    @Autowired
    private DashboardWarningConfigService warningConfigService;

    @Autowired
    private IndexBuildProgressService indexBuildProgressService;

    @Autowired
    private DashboardWarningHistoryDao dashboardWarningHistoryDao;

    @Autowired
    private SearchProjectDao searchProjectDao;

    @Value("${dashboard.report.success.threshold:0.5}")
    private Double successThreshold;

    @Value("${dashboard.report.env}")
    private String env;

    @Resource
    private EntityManager entityManager;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void buildIndexSummaryInfo(Date fromDate, Date toDate) {
        Objects.requireNonNull(fromDate);
        Objects.requireNonNull(toDate);

        Map<Long, DashboardIndexSummaryInfo> indexBuildMap = new HashMap<>();

        Map<Long, SearchProject> projectMap = getProjectMap();

        Map<Long, Long> datasourceNumMap = getDatasourceNumMap();

        List<ProgressAggregateDto> progressAggregateDtoList =
                indexBuildProgressDao.findProgressAggegateDto(IndexBuildType.FULL.value, fromDate, toDate);

        List<Index> indexes = indexDao.findAll();

        Map<Long, Index> indexMapUsingIdAsKey =
                indexes.stream().collect(Collectors.toMap(Index::getId, Function.identity()));

        Map<String, Index> indexMapUsingNameAsKey =
                indexes.stream().collect(Collectors.toMap(Index::getIndexName, Function.identity()));

        for (ProgressAggregateDto aggreDto : progressAggregateDtoList) {
            aggreDto.setIndexId(
                    Optional.ofNullable(indexMapUsingNameAsKey.get(aggreDto.getIndexName())).map(Index::getId).get());
            // 单索引统计 IndexBuildDto
            buildIndexBuildDto(indexBuildMap, aggreDto);
        }
        // 所有索引的cron和预期执行次数
        // 因为定时任务是每天跑一次，所以间隔时间为一天
        fillCornAndExecuteNumsExpect(indexBuildMap, fromDate, toDate);

        // 合计数据
        List<DashboardIndexSummaryInfo> indexBuildTotalDto =
                buildIndexBuildTotalDto(indexBuildMap, projectMap, datasourceNumMap, indexMapUsingIdAsKey);
        for (DashboardIndexSummaryInfo info : indexBuildTotalDto) {
            //以结束时间为数据时间
            info.setDataDate(toDate);
            indexSummaryInfoDao.save(info);
        }
    }

    /**
     * 单索引统计 IndexBuildDto
     *
     * @param indexBuildMap
     * @param aggreDto
     */
    private void buildIndexBuildDto(Map<Long, DashboardIndexSummaryInfo> indexBuildMap, ProgressAggregateDto aggreDto) {
        DashboardIndexSummaryInfo indexBuildDto = indexBuildMap.get(aggreDto.getIndexId());

        if (null == indexBuildDto) {
            indexBuildDto = new DashboardIndexSummaryInfo();
            indexBuildDto.setIndexId(aggreDto.getIndexId());
            indexBuildMap.put(aggreDto.getIndexId(), indexBuildDto);
        }
        // 运行次数
        int frequency = aggreDto.getFrequency().intValue();
        Integer status = aggreDto.getStatus();
        switch (IndexBuildRunType.get(aggreDto.getRunType())) {
            case CRON:// 定时调度总数和成功数
                indexBuildDto.setActualNum(indexBuildDto.getActualNum() + frequency);
                if (IndexBuildStatus.SUCCESS == IndexBuildStatus.get(status)) {
                    indexBuildDto.setActualSuccessNum(frequency);
                }
                break;
            case HANDING:// 手动调度总数和成功数
                indexBuildDto.setManualNum(indexBuildDto.getManualNum() + frequency);
                if (IndexBuildStatus.SUCCESS == IndexBuildStatus.get(status)) {
                    indexBuildDto.setManualSuccessNum(frequency);
                }
                break;
            default:
                break;
        }
        // 导入最高条数
        if (IndexBuildStatus.SUCCESS == IndexBuildStatus.get(status)
                && aggreDto.getImportMaxNum() > indexBuildDto.getRecordNum()) {
            indexBuildDto.setRecordNum(aggreDto.getImportMaxNum().intValue());
        }
        // 辅助计算总量
        if (IndexBuildStatus.SUCCESS == IndexBuildStatus.get(status) && aggreDto.getDatasourceName() == null) {
            indexBuildDto
                    .setRecordTotalNum(indexBuildDto.getRecordTotalNum() + aggreDto.getImportCostNumTotal().intValue());
            indexBuildDto.setRecordTotalCost(
                    indexBuildDto.getRecordTotalCost() + aggreDto.getImportCostTimeTotal().intValue());
        }
    }

    /**
     * 所有索引的cron和预期执行次数
     *
     * @param indexBuildMapByIdKey
     */
    private void fillCornAndExecuteNumsExpect(Map<Long, DashboardIndexSummaryInfo> indexBuildMapByIdKey,
                                              Date fromDate, Date toDate) {
        // 获取所有的索引
        List<ScheduleTaskDto> scheduleTaskDtos = indexDatasourceScheduleFacade.getAllEffectiveTasks(new TaskQueryDto());
        scheduleTaskDtos.stream().forEach(v -> {
            Long indexId = v.getIndexDatasourceDTOs().get(0).getIndexId();
            try {
                CronExpression cronExpression = new CronExpression(v.getCron());
                CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                        .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)).build();
                if (cronTrigger instanceof CronTriggerImpl) {
                    List<Date> dateList = TriggerUtils.computeFireTimesBetween((CronTriggerImpl) cronTrigger, null,
                            fromDate, toDate);
                    if (dateList.size() > 0) {
                        DashboardIndexSummaryInfo indexBuildDto = indexBuildMapByIdKey.get(indexId);
                        if (indexBuildDto == null) {
                            indexBuildDto = new DashboardIndexSummaryInfo();
                            indexBuildDto.setIndexId(indexId);
                            indexBuildMapByIdKey.put(indexId, indexBuildDto);
                        }
                        indexBuildDto.setExpectNum(dateList.size());
                    }
                }
            } catch (ParseException e) {
                LOG.info("indexId:{} cron表达式:{},计算次数错误", indexId, v.getCron());
            }
        });
    }

    /**
     * 合计数据
     *
     * @param indexBuildMap
     * @param indexMapUseIdKey
     */
    private List<DashboardIndexSummaryInfo> buildIndexBuildTotalDto(Map<Long, DashboardIndexSummaryInfo> indexBuildMap,
                                                                    Map<Long, SearchProject> projectMap, Map<Long, Long> datasourceNumMap, Map<Long, Index> indexMapUseIdKey) {
        List<DashboardIndexSummaryInfo> indexBuildDtos = new ArrayList<>();
        for (Long indexId : indexBuildMap.keySet()) {
            DashboardIndexSummaryInfo dto = indexBuildMap.get(indexId);
            // 平均导入时间
            Integer timeTotal = dto.getRecordTotalCost();
            Integer frequency = dto.getActualSuccessNum() + dto.getManualSuccessNum();
            Integer numTotal = dto.getRecordTotalNum();
            // 单次平均耗时（s）
            dto.setAvgCost(frequency != 0 ? timeTotal / frequency : 0);
            // 单条平均耗时（ms）
            double recordCost = 0D;
            if(numTotal != 0) {
                BigDecimal b = new BigDecimal(1000.0 * timeTotal / numTotal);
                recordCost = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();   
            }
            dto.setRecordCost(recordCost);
            indexBuildDtos.add(dto);
        }
        indexBuildDtos.stream().forEach(s -> {
            Index indexId = indexMapUseIdKey.get(s.getIndexId());
            s.setIndexName(Optional.ofNullable(indexId).map(Index::getIndexName).orElse(""));
            s.setProjectId(Optional.ofNullable(indexId).map(Index::getProjectId).orElse(0L));
            s.setProjectName(Optional.ofNullable(projectMap.get(s.getProjectId())).map(SearchProject::getProjectName)
                    .orElse(""));
            s.setDatasourceNum(Optional.ofNullable(datasourceNumMap.get(s.getIndexId())).map(Long::intValue).orElse(0));
        });
        return indexBuildDtos;
    }

    /**
     * 索引视图预览
     */
    @Override
    @Transactional(readOnly = true)
    public PageDTO<DashboardIndexSummaryInfoQueryResult> listPage(DashboardIndexSummaryInfoDTO dto, PageQueryDTO pageQueryDto) {

        Page<DashboardIndexSummaryInfoQueryResult> page = indexSummaryInfoDao.getConditionPage(dto, pageQueryDto, entityManager);
        if (page.getTotalElements() == 0) {
            return PageDTO.builder(new ArrayList<DashboardIndexSummaryInfoQueryResult>(0)).build();
        }

        List<DashboardIndexSummaryInfoQueryResult> queryResults = page.getContent().stream().map(x -> {
            
            x.setAvgCost(new BigDecimal(x.getAvgCost()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            
            x.setRecordCost(new BigDecimal(x.getRecordCost()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            
            double rate1 = new BigDecimal(Double.valueOf(x.getActualSuccessRate() * 100)).
                    setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            x.setActualSuccessRate(rate1);

            double rate2 = new BigDecimal(Double.valueOf(x.getManualSuccessRate() * 100)).
                    setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            x.setManualSuccessRate(rate2);
            return x;
        }).collect(Collectors.toList());

        return PageDTO.builder(queryResults).pageNo(pageQueryDto.getPageNo()).pageSize(pageQueryDto.getPageSize())
                .totalPages(page.getTotalPages()).totalRecords(page.getTotalElements()).build();
    }

    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> indexViewByIndex(DashboardIndexSummaryInfoDTO dto, PageQueryDTO pageQueryDto) {
        DashboardIndexRequestDTO indexRequestDTO = new DashboardIndexRequestDTO();

        //通过项目Id获取项目code
        Long projectId = dto.getProjectId();
        SearchProject project = searchProjectDao.getOne(projectId);
        if (project != null) {
            indexRequestDTO.setProjectCode(project.getProjectCode());
        }

        //获取索引集合
        List<Index> indices = indexDao.findByIdInAndStatus(dto.getIndexId(), Status.NORMAL.getValue());
        if (CollectionUtils.isNotEmpty(indices)) {
            List<String> strings = indices.stream().map(x -> x.getIndexName()).collect(Collectors.toList());
            indexRequestDTO.setIndexNames(strings);
        }

        //获取构建执行列表
        Page<IndexBuildProgressItemDTO> page = indexSummaryInfoDao.listPageByIndex(dto, indexRequestDTO, pageQueryDto,
                entityManager);

        List<IndexBuildProgressItemDTO> queryResults = page.getContent();

        //图表-1
        List<ChartReportXYDTO> costXyMapDTOS = new LinkedList<>();

        //获取索引数量集合
        Map<Long, Long> datasourceNumMap = getDatasourceNumMap();

        queryResults.stream().map(x -> {
            String name = x.getIndexName();
            ChartReportXYDTO costXyMapDTO = new ChartReportXYDTO();
            costXyMapDTO.setName(name);
            costXyMapDTO.setX(x.getId() + "");
            costXyMapDTO.setY(x.getRecordTotalCost());
            costXyMapDTOS.add(costXyMapDTO);

            IndexBuildStatus buildStatus = IndexBuildStatus.get(x.getStatus());
            if (buildStatus != null) {
                x.setStatusFommat(buildStatus.desc);
            }
            IndexBuildRunType buildRunType = IndexBuildRunType.get(x.getRunType());
            if (buildRunType != null) {
                x.setRunTypeFomamt(buildRunType.desc);
            }
            x.setAvgCost(new BigDecimal(x.getAvgCost()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            
            x.setProjectName(project.getProjectName());
            x.setDatasourceNum(datasourceNumMap.getOrDefault(projectId, 0L));
            return x;
        }).collect(Collectors.toList());

        //分页DTO
        PageDTO<IndexBuildProgressItemDTO> pageDTO = PageDTO.builder(queryResults).pageNo(pageQueryDto.getPageNo()).
                pageSize(pageQueryDto.getPageSize()).totalPages(page.getTotalPages()).totalRecords(page.getTotalElements()).build();

        Map<String, List<ChartReportXYDTO>> costMap = costXyMapDTOS.stream().collect(Collectors.groupingBy(
                ChartReportXYDTO::getName, Collectors.toList()
        ));

        List<ChartReportResultDTO> costList = costMap.entrySet().stream().map(x -> {
            ChartReportResultDTO chartReportResultDTO = new ChartReportResultDTO();
            chartReportResultDTO.setName(x.getKey());
            List<ChartReportXYMapDTO> list2 = x.getValue().stream().map(t -> {
                ChartReportXYMapDTO chartReportXYMapDTO = new ChartReportXYMapDTO();
                chartReportXYMapDTO.setX(t.getX());
                chartReportXYMapDTO.setY(t.getY());
                return chartReportXYMapDTO;
            }).collect(Collectors.toList());
            //根据X轴升序
            Collections.sort(list2, new Comparator<ChartReportXYMapDTO>() {
                @Override
                public int compare(ChartReportXYMapDTO a, ChartReportXYMapDTO b) {
                    Long ax = Long.parseLong(a.getX());
                    Long bx = Long.parseLong(b.getX());
                    return ax.compareTo(bx);
                }
            });
            chartReportResultDTO.setXyMapList(list2);
            return chartReportResultDTO;
        }).collect(Collectors.toList());

        //图表-2
        List<ChartReportXYDTO> avgCostXyMapDTOS = new LinkedList<>();
        List<IndexChartItemDTO> charList = indexSummaryInfoDao.avgCostByIndex(dto, pageQueryDto, entityManager);

        charList.stream().map(x -> {
            String name = x.getIndexName();
            ChartReportXYDTO costXyMapDTO = new ChartReportXYDTO();
            costXyMapDTO.setName(name);
            costXyMapDTO.setX(DateUtils.getFormatDate(x.getDataDate()));
            costXyMapDTO.setY(new BigDecimal(x.getAvgCost()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            avgCostXyMapDTOS.add(costXyMapDTO);
            return x;
        }).collect(Collectors.toList());


        Map<String, List<ChartReportXYDTO>> avgCostMap = avgCostXyMapDTOS.stream().collect(Collectors.groupingBy(
                ChartReportXYDTO::getName, Collectors.toList()
        ));

        List<ChartReportResultDTO> avgCostList = avgCostMap.entrySet().stream().map(x -> {
            ChartReportResultDTO chartReportResultDTO = new ChartReportResultDTO();
            chartReportResultDTO.setName(x.getKey());
            List<ChartReportXYMapDTO> list2 = x.getValue().stream().map(t -> {
                ChartReportXYMapDTO chartReportXYMapDTO = new ChartReportXYMapDTO();
                chartReportXYMapDTO.setX(t.getX());
                chartReportXYMapDTO.setY(t.getY());
                return chartReportXYMapDTO;
                //根据X轴升序
            }).collect(Collectors.toList());
            chartReportResultDTO.setXyMapList(list2);
            return chartReportResultDTO;
        }).collect(Collectors.toList());

        Map<String, Object> resultDTOMap = new HashMap<>();
        resultDTOMap.put("indexCost", costList);
        resultDTOMap.put("indexAvgCost", avgCostList);
        resultDTOMap.put("pageDto", pageDTO);
        return resultDTOMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void monitorIndexBuild(Date startTime, Date endTime) {
        List<IndexBuildProgressDTO> progressDTOs = indexBuildProgressService.queryByStatusAndUpdateTime(startTime, endTime);
        Map<Long, Map<Long, Map<String, Object>>> progressMap = getIndicatorMap(getProgressMap(progressDTOs));

        List<DashboardWarningConfig> warningConfigs =
                warningConfigService.findByWarningTypeAndStatus(WarningType.INDEX.getValue());
        for (DashboardWarningConfig config : warningConfigs) {
            LOG.info("handle DashboardWarningConfig, id : {} , warningConfig : {}", config.getId(), config.getWarningConfig());

            Long project = config.getProjectId();

            // 没有project的构建记录，直接跳过
            if (!progressMap.containsKey(project)) {
                continue;
            }

            Map<Long, Map<String, Object>> indexMap = progressMap.get(project);
            String index = config.getIndexId();

            // 如果index为空，则表示项目下索引全部，否则逗号分隔字符串
            if ("-1".equals(index)) {
                for (Entry<Long, Map<String, Object>> entry : indexMap.entrySet()) {
                    handle(entry, config);
                }
            } else {
                List<String> indexList = Arrays.asList(index.split(","));
                for (Entry<Long, Map<String, Object>> entry : indexMap.entrySet()) {
                    if (indexList.contains(entry.getKey().toString())) { // 有indexName构建记录
                        try {
                            handle(entry, config);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    private void handle(Entry<Long, Map<String, Object>> entry, DashboardWarningConfig config) {
        Long index = entry.getKey();
        Map<String, Object> indicatorMap = entry.getValue();
        JSONObject jb = JSONObject.parseObject(config.getWarningConfig());
        boolean recordCostFlag = jb.containsKey(MonitorIndicator.INDEX.RECORD_COST);
        boolean indexCostFlag = jb.containsKey(MonitorIndicator.INDEX.INDEX_COST);
        boolean successRateFlag = jb.containsKey(MonitorIndicator.INDEX.SUCCESS_RATE);
        boolean flag = false;
        JSONObject warningTrigger = new JSONObject();

        if (recordCostFlag) {
            Long temp = (Long) indicatorMap.get(MonitorIndicator.INDEX.RECORD_COST);
            warningTrigger.put(MonitorIndicator.INDEX.RECORD_COST, temp.intValue());
            int rs = jb.getInteger(MonitorIndicator.INDEX.RECORD_COST);
            if ((rs > 0) && (temp.intValue() > rs)) {
                flag = true;
            }
        }
        if (indexCostFlag) {
            Long temp = (Long) indicatorMap.get(MonitorIndicator.INDEX.INDEX_COST);
            warningTrigger.put(MonitorIndicator.INDEX.INDEX_COST, temp);
            int ic = jb.getInteger(MonitorIndicator.INDEX.INDEX_COST);
            if ((ic > 0) && (temp > ic)) {
                flag = true;
            }
        }
        if (successRateFlag) {
            int temp = (Integer) indicatorMap.get(MonitorIndicator.INDEX.SUCCESS_RATE);
            warningTrigger.put(MonitorIndicator.INDEX.SUCCESS_RATE, temp);
            int sr = jb.getInteger(MonitorIndicator.INDEX.SUCCESS_RATE);
            if ((sr > 0) && (sr <= 100) && (temp < sr)) {
                flag = true;
            }
        }
        if (flag) {
            // save
            String indexName = indicatorMap.get(MonitorIndicator.INDEX_NAME).toString();

            LOG.info("trigger! indexName : {}, warningTrigger : {} ", indexName, warningTrigger.toJSONString());

            DashboardWarningHistory warningHistory = new DashboardWarningHistory();
            warningHistory.setIndexId(index);
            warningHistory.setIndexName(indexName);
            warningHistory.setProjectId(config.getProjectId());
            warningHistory.setProjectName(config.getProjectName());
            warningHistory.setConfigId(config.getId());
            warningHistory.setWarningTrigger(warningTrigger.toJSONString());
            dashboardWarningHistoryDao.save(warningHistory);

            // send
            MailDto<WarningModelDTO> mailDto = new MailDto<>();
            mailDto.setTemplateName(MailConstants.INDEX_WARNING_TEMPLATE);
            mailDto.setEmailTo(Arrays.asList(config.getMailTo().split(",")));
            mailDto.setSubject(String.format(Constants.REPORT_TEMPLATE_MAIL_TITLE, env, "index预警", ""));

            WarningModelDTO warningModelDTO = new WarningModelDTO();
            warningModelDTO.setProjectName(config.getProjectName());
            warningModelDTO.setIndexName(indexName);
            String template = "单条索引平均耗时 : %sms, 索引耗时 : %ss, 成功率 : %s%%";
            // 配置
            warningModelDTO.setConfig(String.format(template, jb.get(MonitorIndicator.INDEX.RECORD_COST),
                    jb.get(MonitorIndicator.INDEX.INDEX_COST), jb.get(MonitorIndicator.INDEX.SUCCESS_RATE)));
            // 实际
            warningModelDTO.setContent(String.format(template, warningTrigger.get(MonitorIndicator.INDEX.RECORD_COST),
                    warningTrigger.get(MonitorIndicator.INDEX.INDEX_COST),
                    warningTrigger.get(MonitorIndicator.INDEX.SUCCESS_RATE)));
            mailDto.setModel(warningModelDTO);
            mailService.sendMail(mailDto);
        }
    }

    /**
     * 将list数据构建map<项目：<索引：List<索引导入进度>>>
     *
     * @param indexBuildProgressDTOs
     * @return
     */
    private Map<Long, Map<Long, List<IndexBuildProgressDTO>>>
    getProgressMap(List<IndexBuildProgressDTO> indexBuildProgressDTOs) {

        List<SearchProject> projectList = searchProjectDao.findAll();
        Map<String, Long> projectMap =
                projectList.stream().collect(Collectors.toMap(SearchProject::getProjectCode, SearchProject::getId));

        List<Index> indexes = indexDao.findAll();
        Map<String, Long> indexMapUsingNameAsKey =
                indexes.stream().collect(Collectors.toMap(Index::getIndexName, Index::getId));

        Map<Long, Map<Long, List<IndexBuildProgressDTO>>> progressMap = new HashMap<>();
        for (IndexBuildProgressDTO dto : indexBuildProgressDTOs) {

            Long project = projectMap.get(dto.getProjectCode()); // 项目code转为项目id
            Long index = indexMapUsingNameAsKey.get(dto.getIndexName());// 索引名称转为索引id

            Map<Long, List<IndexBuildProgressDTO>> indexMap = null;
            if (progressMap.containsKey(project)) {
                indexMap = progressMap.get(project);
            } else {
                indexMap = new HashMap<>();
                progressMap.put(project, indexMap);
            }
            if (indexMap.containsKey(index)) {
                List<IndexBuildProgressDTO> list = indexMap.get(index);
                list.add(dto);
            } else {
                List<IndexBuildProgressDTO> list = new ArrayList<>();
                list.add(dto);
                indexMap.put(index, list);
            }
        }
        return progressMap;
    }

    /**
     * 将以上返回的数据直接处理成指标数据map<项目：<索引：<指标：数值>>>
     *
     * @param projectMap
     * @return
     */
    private Map<Long, Map<Long, Map<String, Object>>>
    getIndicatorMap(Map<Long, Map<Long, List<IndexBuildProgressDTO>>> projectMap) {
        // 按照项目索引计算监控指标
        Map<Long, Map<Long, Map<String, Object>>> result = new HashMap<>();
        for (Entry<Long, Map<Long, List<IndexBuildProgressDTO>>> entryProject : projectMap.entrySet()) {
            Long project = entryProject.getKey();
            Map<Long, Map<String, Object>> indexMap = null;
            if (result.containsKey(project)) {
                indexMap = result.get(project);
            } else {
                indexMap = new HashMap<>();
                result.put(project, indexMap);
            }
            for (Entry<Long, List<IndexBuildProgressDTO>> entryIndex : entryProject.getValue().entrySet()) {
                int successNum = 0; // 构建成功次数
                long recordNum = 0L;// 构建记录总和
                long recordTime = 0L;// 构建总时间
                List<IndexBuildProgressDTO> dtos = entryIndex.getValue();
                for (IndexBuildProgressDTO dto : dtos) {
                    if (dto.getStatus() == IndexBuildStatus.SUCCESS.value) {
                        successNum += 1;
                        recordTime += dto.getExecuteCostTime();
                        recordNum += dto.getImportSuccessCount();
                    }
                }
                Map<String, Object> warningTrigger = new HashMap<>();
                long rc = 0;
                if (recordNum > 0) {
                    rc = recordTime * 1000 / recordNum;
                }
                warningTrigger.put(MonitorIndicator.INDEX.RECORD_COST, rc);
                long ic = 0;
                if (successNum > 0) {
                    ic = recordTime / successNum;
                }
                warningTrigger.put(MonitorIndicator.INDEX.INDEX_COST, ic);
                warningTrigger.put(MonitorIndicator.INDEX.SUCCESS_RATE, successNum * 100 / dtos.size());
                warningTrigger.put(MonitorIndicator.INDEX_NAME, dtos.get(0).getIndexName());
                indexMap.put(entryIndex.getKey(), warningTrigger);
            }
        }
        return result;
    }

    /**
     * 索引失败率top10+索引耗时top10
     *
     * @param startDate 开始时间 yyyy-MM-dd HH:mm:ss
     * @param endDate   结束时间 yyyy-MM-dd HH:mm:ss
     * @return
     */
    @Override
    @Transactional(readOnly = true)
    public DashboardIndexTopQueryResult indexTopView(Date startDate, Date endDate) {

        List<IndexBuildProgressTopDTO> topDTOS = indexBuildProgressService.queryIndexBuildProgressCountList(startDate, endDate);


        //获取索引失败率top10，索引失败率->索引失败次数倒叙
        List<IndexBuildProgressTopDTO> failDTOS = topDTOS.stream().sorted(Comparator.comparing(IndexBuildProgressTopDTO::getFailRate).reversed()
                .thenComparing(Comparator.comparing(IndexBuildProgressTopDTO::getFailNum).reversed()))
                .limit(10)
                .collect(Collectors.toList());

        //获取索引耗时top10，倒叙
        topDTOS.sort((h1, h2) -> h2.getCostTimeTotal().compareTo(h1.getCostTimeTotal()));
        List<IndexBuildProgressTopDTO> costDTOS = topDTOS.stream().limit(10).collect(Collectors.toList());

        //  索引成功率
        long indexSuccessNum = topDTOS.stream().mapToLong(IndexBuildProgressTopDTO::getSuccessNum).sum();
        long indexTotalNum = topDTOS.stream().mapToLong(IndexBuildProgressTopDTO::getExecTotalNum).sum();

        Double indexSuccessRate = Double.parseDouble(indexSuccessNum + "") / indexTotalNum;

        // 索引耗时
        Long indexCostTime = topDTOS.stream().mapToLong(IndexBuildProgressTopDTO::getCostTimeTotal).sum();

        DashboardIndexAggregationDTO aggregationDTO = new DashboardIndexAggregationDTO();

        double rate = new BigDecimal(Double.valueOf(indexSuccessRate * 100)).
                setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();

        aggregationDTO.setIndexSuccessRate(rate);
        aggregationDTO.setIndexCostTime(indexCostTime);

        DashboardIndexTopQueryResult result = new DashboardIndexTopQueryResult();
        result.setDashboardIndexAggregationDTO(aggregationDTO);


        failDTOS = failDTOS.stream().map(x -> {
            SearchProject project = searchProjectDao.findByProjectCode(x.getProjectCode());
            if (project != null) {
                x.setProjectId(project.getId());
                x.setProjectName(project.getProjectName());
            }
            return x;
        }).collect(Collectors.toList());

        result.setIndexFailTop10(failDTOS);

        costDTOS = costDTOS.stream().map(x -> {
            SearchProject project = searchProjectDao.findByProjectCode(x.getProjectCode());
            if (project != null) {
                x.setProjectId(project.getId());
                x.setProjectName(project.getProjectName());
            }
            return x;
        }).collect(Collectors.toList());

        result.setIndexCostTop10(costDTOS);
        return result;
    }

    @Override
    @Transactional(readOnly = true)
    public Map<String, ChartReportResultDTO> indexViewByChart(DashboardIndexSummaryInfoDTO dto) {

        List<DashboardIndexSummaryInfoChartResult> list = indexSummaryInfoDao.getConditionList(dto, entityManager);


        List<ChartReportXYMapDTO> recordCosts = new LinkedList<>();
        List<ChartReportXYMapDTO> successRates = new LinkedList<>();

        list.stream().map(x -> {
            Integer successNum = x.getSuccessNum() == null ? 0 : x.getSuccessNum();
            Integer totalNum = x.getTotalNum() == null ? 0 : x.getTotalNum();

            if (totalNum != 0) {
                x.setSuccessRate(successNum.doubleValue() / totalNum);
            }

            ChartReportXYMapDTO costMapDTO = new ChartReportXYMapDTO();
            costMapDTO.setX(DateUtils.getFormatDate(x.getDataDate()));
            costMapDTO.setY(new BigDecimal(x.getAvgCost()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            recordCosts.add(costMapDTO);

            ChartReportXYMapDTO rateMapDTO = new ChartReportXYMapDTO();
            rateMapDTO.setX(DateUtils.getFormatDate(x.getDataDate()));

            double rate = new BigDecimal(Double.valueOf(x.getSuccessRate() * 100)).
                    setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();

            rateMapDTO.setY(rate);
            successRates.add(rateMapDTO);

            return x;
        }).collect(Collectors.toList());

        //根据X轴排序
        Collections.sort(recordCosts, Comparator.comparing(ChartReportXYMapDTO::getX));
        ChartReportResultDTO costResultDTO = new ChartReportResultDTO();
        costResultDTO.setName("索引平均耗时");
        costResultDTO.setXyMapList(recordCosts);

        //根据X轴排序
        Collections.sort(successRates, Comparator.comparing(ChartReportXYMapDTO::getX));
        ChartReportResultDTO rateResultDTO = new ChartReportResultDTO();
        rateResultDTO.setName("成功率");
        rateResultDTO.setXyMapList(successRates);

        Map<String, ChartReportResultDTO> resultDTOMap = new HashMap<>();
        resultDTOMap.put("indexAvgCost", costResultDTO);
        resultDTOMap.put("indexSuccessRate", rateResultDTO);
        return resultDTOMap;
    }

    @Override
    @Transactional(readOnly = true)
    public PageDTO<DashboardIndexSummaryInfoQueryResult> indexViewByProjectOfPage(DashboardIndexSummaryInfoDTO parmDto, PageQueryDTO pageQueryDTO) {
        Page<DashboardIndexSummaryInfoQueryResult> page = indexSummaryInfoDao.indexViewByProjectOfPage(parmDto, pageQueryDTO, entityManager);
        if (page.getTotalElements() == 0) {
            return PageDTO.builder(new ArrayList<DashboardIndexSummaryInfoQueryResult>(0)).build();
        }

        List<DashboardIndexSummaryInfoQueryResult> queryResults = page.getContent().stream().map(x -> {

            x.setAvgCost(new BigDecimal(x.getAvgCost()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            
            x.setRecordCost(new BigDecimal(x.getRecordCost()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            
            double rate1 = new BigDecimal(Double.valueOf(x.getActualSuccessRate() * 100)).
                    setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();

            x.setActualSuccessRate(rate1);

            double rate2 = new BigDecimal(Double.valueOf(x.getManualSuccessRate() * 100)).
                    setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            x.setManualSuccessRate(rate2);
            return x;
        }).collect(Collectors.toList());

        return PageDTO.builder(queryResults).pageNo(pageQueryDTO.getPageNo()).pageSize(pageQueryDTO.getPageSize())
                .totalPages(page.getTotalPages()).totalRecords(page.getTotalElements()).build();
    }
}